package org.rcy.framework.storage;

import com.obs.services.ObsClient;
import com.obs.services.internal.utils.AccessLoggerUtils;
import com.obs.services.model.AbortMultipartUploadRequest;
import com.obs.services.model.CompleteMultipartUploadRequest;
import com.obs.services.model.GetObjectMetadataRequest;
import com.obs.services.model.GetObjectRequest;
import com.obs.services.model.HttpMethodEnum;
import com.obs.services.model.InitiateMultipartUploadRequest;
import com.obs.services.model.InitiateMultipartUploadResult;
import com.obs.services.model.ObjectMetadata;
import com.obs.services.model.ObsObject;
import com.obs.services.model.PartEtag;
import com.obs.services.model.PutObjectRequest;
import com.obs.services.model.PutObjectResult;
import com.obs.services.model.SpecialParamEnum;
import com.obs.services.model.TemporarySignatureRequest;
import com.obs.services.model.TemporarySignatureResponse;
import com.obs.services.model.UploadPartRequest;
import com.obs.services.model.UploadPartResult;
import org.rcy.framework.storage.huaweicloud.obs.io.HuaweiCloudCipherInputStream;
import org.rcy.framework.storage.huaweicloud.obs.io.HuaweiCloudStorageInputStream;
import org.rcy.framework.storage.huaweicloud.obs.model.FiedObjectMeta;
import org.rcy.framework.storage.huaweicloud.obs.model.PartTag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;


/**
 * @author 吴康桥
 * @Description
 * @date 2023/7/4 11:47
 */


public class HuaweiCloudObsService {
	private static Logger logger;
	private final String bucket;
	private final ObsClient obsClient;

	public ObsClient getOBSClient() {
		return this.obsClient;
	}

	public HuaweiCloudObsService(String accessId, String accessKey, String endpoint, String bucket) {
		this.bucket = bucket;
		this.obsClient = new ObsClient(accessId, accessKey, endpoint);
	}

	public void uploadFile(String key, InputStream input, long contentLength) {
		ObsClient client = null;

		try {
			client = this.getOBSClient();
			ObjectMetadata objectMeta = new ObjectMetadata();
			objectMeta.setContentLength(contentLength);
			PutObjectResult result = client.putObject(this.bucket, key, input, objectMeta);
			this.setRequestId(result);
		} catch (Exception var12) {
			logger.error("storage service uploadFile {} error {}", new Object[]{key, var12.getMessage(), var12});
			throw new RuntimeException("uploadFile error " + key, var12);
		} catch (Throwable var13) {
			logger.error("storage service uploadFile {} throwable {}", new Object[]{key, var13.getMessage(), var13});
			throw new RuntimeException("uploadFile throwable " + key, var13);
		} finally {
			safeCloseClient(client);
			safeClose(input);
		}

	}

	private void setRequestId(PutObjectResult result) {
		try {
			MDC.put("obs_request_id", result.getRequestId());
		} catch (Exception var3) {
			logger.warn("obs setRequestId error {}", var3.getMessage(), var3);
		}

	}

	public InputStream downloadFile(String key) throws IOException {
		ObsClient client = this.getOBSClient();
		ObsObject object = client.getObject(new GetObjectRequest(this.bucket, key));
		return new HuaweiCloudStorageInputStream(object.getObjectContent());
	}

	public InputStream downloadFile(String key, Cipher cipher) throws IOException {
		ObsClient client = this.getOBSClient();
		ObsObject object = client.getObject(new GetObjectRequest(this.bucket, key));
		return new HuaweiCloudCipherInputStream(object.getObjectContent(), cipher);
	}

	public void deleteFile(String fileKey) {
		ObsClient client = this.getOBSClient();
		client.deleteObject(this.bucket, fileKey);
		safeCloseClient(client);
	}

	public String initiateMultipartUpload(String fileKey) {
		ObsClient client = this.getOBSClient();
		InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(this.bucket, fileKey);
		InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
		safeCloseClient(client);
		return result.getUploadId();
	}

	public PartTag uploadPart(String uploadId, String fileKey, InputStream instream, long size, int partNum) {
		ObsClient client = this.getOBSClient();
		UploadPartRequest uploadPartRequest = new UploadPartRequest();
		uploadPartRequest.setBucketName(this.bucket);
		uploadPartRequest.setObjectKey(fileKey);
		uploadPartRequest.setUploadId(uploadId);
		uploadPartRequest.setInput(instream);
		uploadPartRequest.setPartSize(size);
		uploadPartRequest.setPartNumber(partNum);
		UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest);
		safeCloseClient(client);
		return new PartTag(uploadPartResult.getPartNumber(), uploadPartResult.getEtag(), size, (Long)null);
	}

	public void completeMultipartUpload(String uploadId, String fileKey, List<PartTag> partTags) {
		List<PartEtag> partETags = new ArrayList();
		partTags.forEach((p) -> {
			partETags.add(new PartEtag(p.geteTag(), p.getPartNumber()));
		});
		ObsClient client = this.getOBSClient();
		Collections.sort(partETags, new Comparator<PartEtag>() {
			public int compare(PartEtag p1, PartEtag p2) {
				return p1.getPartNumber() - p2.getPartNumber();
			}
		});
		CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(this.bucket, fileKey, uploadId, partETags);
		client.completeMultipartUpload(completeMultipartUploadRequest);
		safeCloseClient(client);
	}

	public void abortMultipartUpload(String uploadId, String fileKey) {
		ObsClient client = this.getOBSClient();
		AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(this.bucket, fileKey, uploadId);
		client.abortMultipartUpload(abortMultipartUploadRequest);
		safeCloseClient(client);
	}

	public void download(String fileKey, String secretKey, int decryptSize, String algorithm, OutputStream outputStream) throws Exception {
		ObsClient client = this.getOBSClient();
		ObsObject object = client.getObject(new GetObjectRequest(this.bucket, fileKey));
		InputStream inputStream = object.getObjectContent();
		Cipher cipher = Cipher.getInstance(algorithm);
		cipher.init(2, new SecretKeySpec(secretKey.getBytes("UTF-8"), algorithm));
		byte[] buffer = new byte[decryptSize];

		int len;
		byte[] enBtyes;
		for(len = 0; (len = inputStream.read(buffer)) != -1; outputStream.write(enBtyes)) {
			enBtyes = null;
			if (len >= decryptSize) {
				enBtyes = cipher.doFinal(buffer, 0, len);
			} else {
				ByteArrayOutputStream output = new ByteArrayOutputStream();
				output.write(buffer, 0, len);

				for(byte[] buffer2 = new byte[decryptSize - output.size()]; (len = inputStream.read(buffer2)) != -1; buffer2 = new byte[decryptSize - output.size()]) {
					output.write(buffer2, 0, len);
					if (output.size() == decryptSize) {
						break;
					}
				}

				enBtyes = cipher.doFinal(output.toByteArray());
			}
		}

		safeCloseClient(client);
	}

	public FiedObjectMeta getSimplifiedObjectMeta(String fileKey) {
		ObsClient client = this.getOBSClient();
		ObjectMetadata metadata = client.getObjectMetadata(new GetObjectMetadataRequest(this.bucket, fileKey));
		safeCloseClient(client);
		FiedObjectMeta fiedObjectMeta = new FiedObjectMeta();
		fiedObjectMeta.setLastModified(metadata.getLastModified());
		fiedObjectMeta.setSize(metadata.getContentLength());
		return fiedObjectMeta;
	}

	public boolean exist(String fileKey) {
		ObsClient client = this.getOBSClient();
		return client.doesObjectExist(this.bucket, fileKey);
	}

	public void uploadLocalFileAndSetName(String key, String filePath, String downloadName) {
		ObsClient client = this.getOBSClient();
		PutObjectRequest putObjectRequest = new PutObjectRequest(this.bucket, key, new File(filePath));
		ObjectMetadata objectMeta = new ObjectMetadata();

		try {
			downloadName = URLEncoder.encode(downloadName, "UTF-8");
		} catch (Exception var8) {
		}

		objectMeta.setContentDisposition("attachment; filename=\"" + downloadName + "\"");
		putObjectRequest.setMetadata(objectMeta);
		client.putObject(putObjectRequest);
		safeCloseClient(client);
	}

	public URL generateDownloadFileUrl(String fileKey, Date expireDate) {
		long expireSeconds = (expireDate.getTime() - (new Date()).getTime()) / 1000L;
		if (expireSeconds <= 0L) {
			expireSeconds = 600L;
		}

		TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, this.bucket, fileKey, (SpecialParamEnum)null, expireSeconds);
		ObsClient client = this.getOBSClient();
		TemporarySignatureResponse response = client.createTemporarySignature(request);
		String urlString = response.getSignedUrl();
		safeCloseClient(client);

		try {
			return new URL(urlString);
		} catch (MalformedURLException var10) {
			throw new RuntimeException("generateUrl error");
		}
	}

	private static void safeClose(InputStream inputStream) {
		if (inputStream != null) {
			try {
				inputStream.close();
			} catch (IOException var2) {
			}
		}

	}

	private static void safeCloseClient(ObsClient client) {
	}

	static {
		AccessLoggerUtils.accesslogEnabled = false;
		logger = LoggerFactory.getLogger(HuaweiCloudObsService.class);
	}
}
